package com.tenclass.common.configuration;

import com.tenclass.common.interceptor.exception.ContextExceptionV2Resolver;
import com.tenclass.common.interceptor.exception.ExceptionHandlerAdvice;
import com.tenclass.common.interceptor.response.WrapperResponseHandler;
import com.tenclass.common.properties.XXLJobProperties;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import io.micrometer.core.instrument.MeterRegistry;
import io.sentry.SentryClient;
import io.sentry.SentryClientFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;

/**
 * 应用配置自动装配类
 * @author YIJIUE
 * @since 1.1.0
 */
@Configuration
@ConditionalOnMissingBean(name = "customApplicationConfiguration")
@EnableConfigurationProperties(XXLJobProperties.class)
public class ApplicationConfiguration {

    private final Logger logger = LoggerFactory.getLogger(ApplicationConfiguration.class);

    @Bean
    @ConditionalOnMissingBean(name = "invalidContextExceptionResolver")
    public ContextExceptionV2Resolver encodeContext() {
        return new ContextExceptionV2Resolver();
    }

    @Bean
    @ConditionalOnMissingBean
    MeterRegistryCustomizer<MeterRegistry> configurer(
            @Value("${spring.application.name:unknown}") String applicationName) {
        return (registry) -> registry.config().commonTags("application", applicationName);
    }

    @Bean
    @ConditionalOnMissingBean
    SentryClient sentryClient(@Value("${sentry.dsn:unknown}") String dsn, @Value("${sentry.env:unknown}") String env) {
        SentryClient sentryClient = SentryClientFactory.sentryClient(dsn.equals("unknown") ? "" : dsn);
        sentryClient.setEnvironment(env);
        return sentryClient;
    }

    /**
     * 初始化自动封装 handler 替代 {@link RequestResponseBodyMethodProcessor}
     * @param requestMappingHandlerAdapter 请求适配器
     * @return InitializingBean
     */
    @Bean
    @Resource
    @ConditionalOnMissingBean(name = "simpleWrapperRequestHandler")
    public InitializingBean wrapperResponseHandler(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
        return () -> {
            List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();
            List<HandlerMethodReturnValueHandler> newHandlers = new LinkedList<>();

            if (!CollectionUtils.isEmpty(returnValueHandlers)) {
                for (int i = 0; i < returnValueHandlers.size(); i++) {
                    final HandlerMethodReturnValueHandler valueHandler = returnValueHandlers.get(i);
                    if (RequestResponseBodyMethodProcessor.class.isAssignableFrom(valueHandler.getClass())) {
                        newHandlers.add(i, new WrapperResponseHandler(requestMappingHandlerAdapter.getMessageConverters()));
                    }
                    newHandlers.add(valueHandler);
                }
                requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);
            }
        };
    }

    @Bean
    @ConditionalOnMissingBean(name = "globalExceptionHandler")
    public ExceptionHandlerAdvice exceptionHandler() {
        return new ExceptionHandlerAdvice();
    }

    @Bean
    @ConditionalOnProperty(name = "xxl.job.enabled", havingValue = "true")
    public XxlJobSpringExecutor xxlJobInit(XXLJobProperties xxlJobProperties) {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());
        xxlJobSpringExecutor.setAppname(xxlJobProperties.getExecutorAppname());
        xxlJobSpringExecutor.setAddress(xxlJobProperties.getExecutorAddress());
        xxlJobSpringExecutor.setIp(xxlJobProperties.getExecutorIp());
        xxlJobSpringExecutor.setPort(xxlJobProperties.getExecutorPort());
        xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());
        xxlJobSpringExecutor.setLogPath(xxlJobProperties.getExecutorLogPath());
        xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getExecutorLogRetentionDays());

        return xxlJobSpringExecutor;
    }

}
